{**************************************************}
{                                                  }
{         Delphi VCL Extensions                    }
{                                                  }
{ Copyright (c) 1998-2001 Developer Express Inc.   }
{           All Rights Reserved                    }
{                                                  }
{**************************************************}

unit CHDesigner;

interface

uses Classes, Graphics, Forms, Windows, Controls, Messages, Menus, SysUtils,
     StdCtrls, ExtCtrls, AYConst,Dialogs;

type
  TCHDesigner = class;

  TCHDsCount = class(TList)
  private
    FActiveDesigner: TCHDesigner;
  protected
    function Get(Index: Integer): TCHDesigner;
    procedure Put(Index: Integer; Item: TCHDesigner);
  public
    constructor Create;
    destructor  Destroy; override;
    procedure Delete(ADesigner: TCHDesigner); overload;
    function DesignerInControl(AControl: TControl): Boolean;
    function GetActiveControl: TControl;
    property Items[Index: Integer]: TCHDesigner read Get write Put; default;
    property ActiveDesigner: TCHDesigner read FActiveDesigner write FActiveDesigner;
  end;

  TCHDesignPopupMenu = class(TPopupMenu)
  protected
    procedure DoPopup(Sender: TObject); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function MenuItemByTag(ATag: Integer): TMenuItem;
    function MenuItemByCaption(AItem: TMenuItem; ACaption: String): TMenuItem;
  end;

  TCHControlList = class(TList)
  protected
    function Get(Index: Integer): TControl;
    procedure Put(Index: Integer; Item: TControl);
  public
    constructor Create;
    destructor Destroy; override;
    procedure ClearControls;
    property Items[Index: Integer]: TControl read Get write Put; default;
  end;

  TDesignerDeleteControl = procedure (Sender, DelObject: TObject) Of Object;
  TDesignerGetComponentsControl = procedure (Sender: TObject; var AControl: TWinControl) Of Object;
  TDesignerCreateCHControl = procedure (Sender: TObject; X, Y: Integer; var ASuccess: Boolean) Of Object;
  TDesignerDesignCHControl = procedure (Sender: TObject; ADesign: Boolean) Of Object;
  TDesignerSelectCHControl = procedure (Sender: TObject; ASelection: TCHControlList) Of Object;
  TDesignerReSizeSelection = procedure (Sender: TObject; AMode: Integer; var ALeft, ATop, AWidth, AHeight: Integer) Of Object;
  TDesignerCHControlMove   = procedure (Sender: TObject; var ALeft, ATop: Integer) Of Object;

  TCHDesigner = class(TComponent)
  private
    FActive: Boolean;
    FPause: Boolean;
    FComponentsControl: TWinControl;
    FStepToGrid: Integer;
    FDownPoint : TPoint;
    FStartP: TPoint;
    FEndP:   TPoint;
    FSelection: TCHControlList;
    FSortList: TStringList;
    FMoveControl: TControl;
    FTabControl: TControl;
    FPopupControl: TControl;
    FMode: Integer;
    FReSizeEdgeWidth: Integer;
    FPopupMenuImages: TImageList;
    FPopupMenu: TCHDesignPopupMenu;
    FFmInspector: TForm;
    FUsePopupMenu: Boolean;
    FUseObjectInspector: Boolean;
    FDesignClassNames: TStrings;
    FOnDeleteControl: TDesignerDeleteControl;
    FOnGetComponentsControl: TDesignerGetComponentsControl;
    FOnCreateCHControl: TDesignerCreateCHControl;
    FOnDesignCHControl: TDesignerDesignCHControl;
    FOnSelectCHControl: TDesignerSelectCHControl;
    FOnReSizeSelection: TDesignerReSizeSelection;
    FCopyList: TCHControlList;
    FOnCHControlMove: TDesignerCHControlMove;
    { FSelection Functons }
    function IsSelection(AControl: TControl): Boolean;
    function IsCHWinControl(AControl: TControl): Boolean;
    function MoveSelection(Dx, Dy: Integer; var P: TPoint; KeyMove: Boolean = False): Boolean;
    function ReSizeSelection(Dx, Dy: Integer; var P: TPoint; KeyMove: Boolean = False): Boolean;
    function ExistsCHName(AName: String; AControl: TControl): Boolean;
    procedure CopySelectionControlRecursion(AControl: TControl; AParent: TWinControl; ACopy: Boolean);
    procedure PasteControlRecursion(AControl: TControl; AParent: TWinControl; ASelection: Boolean);
    function GetComponentsControl: TWinControl;
    procedure SetComponentsControl(const Value: TWinControl);
    procedure GetSelRectControls(AWinControl: TWinControl);
    procedure GetSelRectControlsRecursion(AWinControl: TWinControl);
    procedure GetKeyMovePos(Key: Word; var Dx, Dy: Integer; bShiftCtrl: Boolean);
    procedure GetKeySizePos(Key: Word; var Dx, Dy, pMode: Integer);

    procedure SetStepToGrid(Value: Integer);
    procedure SetActive(Value: Boolean);
    function GetSelRect(SP, EP: TPoint): TRect;
    procedure CHDrawFocusRect(R: TRect);
    procedure ApplicationMessages(var Msg: TMsg; var Handled: Boolean);
    procedure SetMyCursor(AControl: TControl; Mode: Integer);
    procedure PopupMenuClick(Sender: TObject);
    function AddSeparatorPopupMenu(APopupMenu: TCHDesignPopupMenu; AParent: TMenuItem): TMenuItem;
    function AddControlDesignPopupMenu(APopupMenu: TCHDesignPopupMenu; AParent: TMenuItem;
                                       ACaption: String; AAction: Integer): TMenuItem;
    procedure AddControlPopupMenu(APopupMenu: TCHDesignPopupMenu);
    procedure AddPositionPopupMenu(APopupMenu: TCHDesignPopupMenu);
    procedure AddTabOrderPopupMenu(APopupMenu: TCHDesignPopupMenu);
    procedure AddCopyPastePopupMenu(APopupMenu: TCHDesignPopupMenu);
    procedure InitPopupMenu(APopupMenu: TCHDesignPopupMenu);
    procedure ObjectListChange(Sender: TObject);
    function InComponentsControl(AHandle: HWnd): Boolean;
    function FindCHControl(AWinControl: TWinControl; P: TPoint): TControl;
    function FindMoveControl(AControl: TControl; P: TPoint; var Mode: Integer): TControl;
    function IsCHClass(AControl: TControl): Boolean;
    procedure SetDesignClassNames(Value: TStrings);
    function GetOwnerControl: TControl;
    function GetFmInspector: TForm;
    function GetCurrentContainer: TWinControl;
    function GetObjectHisName(AObject: TObject): String;
    procedure InspectorSortList(Sender: TObject);
  protected
    function IsContainer(AControl: TControl): Boolean;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    property OwnerControl: TControl read GetOwnerControl;
    property ComponentsControl: TWinControl read GetComponentsControl write SetComponentsControl;
    property CurrentContainer: TWinControl read GetCurrentContainer;
    property FmInspector: TForm read GetFmInspector;
    property CopyList: TCHControlList read FCopyList;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure RefreshObjectList;
    procedure ControlProperty(bForce: Boolean = True);
    procedure InitDesigner;
    function AddSelection(AControl: TControl): Integer;
    procedure DelSelection(AControl: TControl; DelAll: Boolean = False);
    procedure CopySelectionControl;
    procedure PasteControl;
    procedure DeleteSelectionControl;
    { FPopuMenu Procedure }
    procedure ControlAlignToGrid;
    procedure ControlBringToFront;
    procedure ControlSendToBack;
    procedure ControlAlign;
    procedure ControlSize;
    procedure ControlTabOrder;
    procedure ControlDesigning;

    property Selection: TCHControlList read FSelection;
  published
    property StepToGrid: Integer read FStepToGrid write SetStepToGrid default 8;
    property Active: Boolean read FActive write SetActive default False;
    property Pause: Boolean read FPause write FPause default False;
    property ReSizeEdgeWidth: Integer read FReSizeEdgeWidth write FReSizeEdgeWidth default 5;
    property UsePopupMenu: Boolean read FUsePopupMenu write FUsePopupMenu default True;
    property UseObjectInspector: Boolean read FUseObjectInspector write FUseObjectInspector default True;
    property DesignClassNames: TStrings read FDesignClassNames write SetDesignClassNames;
    property OnDeleteControl: TDesignerDeleteControl read FOnDeleteControl write FOnDeleteControl;
    property OnGetComponentsControl: TDesignerGetComponentsControl read FOnGetComponentsControl write FOnGetComponentsControl;
    property OnCreateCHControl: TDesignerCreateCHControl read FOnCreateCHControl write FOnCreateCHControl;
    property OnDesignCHControl: TDesignerDesignCHControl read FOnDesignCHControl write FOnDesignCHControl;
    property OnSelectCHControl: TDesignerSelectCHControl read FOnSelectCHControl write FOnSelectCHControl;
    property OnReSizeSelection: TDesignerReSizeSelection read FOnReSizeSelection write FOnReSizeSelection;
    property OnCHControlMove: TDesignerCHControlMove read FOnCHControlMove write FOnCHControlMove;
  end;

  TCHClassName = class of TWinControl;

var
  CHDsCount: TCHDsCount;

implementation
  Uses CHEdit, CHDateTime, CHCheckList, Grids, CHGrid, CHLabel, CHConst,
       CHAlignment, CHSize, CHTabOrder, Math, CHInspector,
       CHImage, TypInfo, Clipbrd, rmConst, CHSeal, EleCustomEdit, EleEdit,
       LDSLabel,LDSEdit,LDSPanel;

Const
  ControlMax = 2048;
  ControlMin = 10;

  { MenuItemIndex }
  miAlignToGrid   = 0;
  miBringToFront  = 1;
  miSendToBack    = 2;
  miSeparator     = 3;
  miAlign         = 4;
  miSize          = 5;
  miTabOrder      = 6;
  miObjectProperty= 7;
  miParentMenu    = 8;
  miDesignObject  = 9;
  miCHGridSetting = 10;
  miCHImageSetting= 11;
  miControlMenu   = 12;
  miPositionMenu  = 13;
  miCopyControl   = 14;
  miPasteControl  = 15;
  miCutControl    = 16;

{ TCHDsCount }
constructor TCHDsCount.Create;
begin
 inherited;
 FActiveDesigner := Nil;
end;

destructor TCHDsCount.Destroy;
begin
  inherited;
end;

function TCHDsCount.Get(Index: Integer): TCHDesigner;
begin
  Result := TCHDesigner(inherited Get(Index));
end;

procedure TCHDsCount.Put(Index: Integer; Item: TCHDesigner);
begin
  inherited Put(Index, Item);
end;

procedure TCHDsCount.Delete(ADesigner: TCHDesigner);
var
  i: Integer;
begin
  i := IndexOf(ADesigner);
  if i >= 0 then
  begin
    if ActiveDesigner = ADesigner then
      ActiveDesigner := Nil;
    Delete(i);
  end;
end;

function TCHDsCount.DesignerInControl(AControl: TControl): Boolean;
var
  i: Integer;
begin
  Result := False;
  With AControl Do begin
    For i := 0 To ComponentCount - 1 Do begin
      if Components[i] is TCHDesigner then
      begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

function TCHDsCount.GetActiveControl: TControl;
begin
  Result := Nil;
  if Assigned(CHDsCount.ActiveDesigner) then
  begin
    Result := TControl(CHDsCount.ActiveDesigner.Owner);
  end;
end;

{ TCHDesigner }

function TCHDesigner.AddSeparatorPopupMenu(APopupMenu: TCHDesignPopupMenu; AParent: TMenuItem): TMenuItem;
begin
  With APopupMenu Do begin
    Result := TMenuItem.Create(APopupMenu);
    if Assigned(AParent) then
      AParent.Add(Result)
    else
      Items.Add(Result);
    Result.Caption := '-';
    Result.Tag := miSeparator;
  end;
end;

function TCHDesigner.AddControlDesignPopupMenu(APopupMenu: TCHDesignPopupMenu; AParent: TMenuItem;
                                               ACaption: String; AAction: Integer): TMenuItem;
begin
  With APopupMenu Do begin
    Result := TMenuItem.Create(APopupMenu);
    if Assigned(AParent) then
      AParent.Add(Result)
    else
      Items.Add(Result);
    Result.Caption := ACaption;
    Result.Tag := AAction;
    Result.OnClick := PopupMenuClick;
  end;
end;

procedure TCHDesigner.AddControlPopupMenu(APopupMenu: TCHDesignPopupMenu);
var
  oParent, oItem: TMenuItem;
begin
  With APopupMenu Do begin
    oParent := TMenuItem.Create(APopupMenu);
    Items.Add(oParent);
    oParent.Caption := 'Control';
    oParent.Tag := miControlMenu;

    oItem := TMenuItem.Create(APopupMenu);
    oParent.Add(oItem);
    oItem.Caption := 'Bring To &Front';
    oItem.Tag := miBringToFront;
    oItem.OnClick := PopupMenuClick;

    oItem := TMenuItem.Create(APopupMenu);
    oParent.Add(oItem);
    oItem.Caption := 'Send To &Back';
    oItem.Tag := miSendToBack;
    oItem.OnClick := PopupMenuClick;
  end;
end;

procedure TCHDesigner.AddPositionPopupMenu(APopupMenu: TCHDesignPopupMenu);
var
  oParent, oItem: TMenuItem;
begin
  With APopupMenu Do begin
    oParent := TMenuItem.Create(APopupMenu);
    Items.Add(oParent);
    oParent.Caption := 'Position';
    oParent.Tag := miPositionMenu;

    oItem := TMenuItem.Create(APopupMenu);
    oParent.Add(oItem);
    oItem.Caption := 'Align To &Grid';
    oItem.Tag := miAlignToGrid;
    oItem.OnClick := PopupMenuClick;

    AddSeparatorPopupMenu(APopupMenu, Nil);

    oItem := TMenuItem.Create(APopupMenu);
    oParent.Add(oItem);
    oItem.Caption := '&Align...';
    oItem.Tag := miAlign;
    oItem.OnClick := PopupMenuClick;

    oItem := TMenuItem.Create(APopupMenu);
    oParent.Add(oItem);
    oItem.Caption := '&Size...';
    oItem.Tag := miSize;
    oItem.OnClick := PopupMenuClick;
  end;
end;

procedure TCHDesigner.AddTabOrderPopupMenu(APopupMenu: TCHDesignPopupMenu);
var
  oItem: TMenuItem;
begin
  With APopupMenu Do begin
    oItem := TMenuItem.Create(APopupMenu);
    Items.Add(oItem);
    oItem.Caption := 'Tab &Order...';
    oItem.Tag := miTabOrder;
    oItem.OnClick := PopupMenuClick;
    
    AddSeparatorPopupMenu(APopupMenu, Nil);
  end;
end;

procedure TCHDesigner.AddCopyPastePopupMenu(APopupMenu: TCHDesignPopupMenu);
var
  oItem: TMenuItem;
begin
  With APopupMenu Do begin
    oItem := TMenuItem.Create(APopupMenu);
    Items.Add(oItem);
    oItem.Caption := 'Copy Control';
    oItem.Tag := miCopyControl;
    oItem.OnClick := PopupMenuClick;

    AddSeparatorPopupMenu(APopupMenu, Nil);
    
    oItem := TMenuItem.Create(APopupMenu);
    Items.Add(oItem);
    oItem.Caption := 'Paste Control';
    oItem.Tag := miPasteControl;
    oItem.OnClick := PopupMenuClick;

    AddSeparatorPopupMenu(APopupMenu, Nil);
    
    oItem := TMenuItem.Create(APopupMenu);
    Items.Add(oItem);
    oItem.Caption := 'Cut Control';
    oItem.Tag := miCutControl;
    oItem.OnClick := PopupMenuClick;
  end;
end;

procedure TCHDesigner.InitPopupMenu(APopupMenu: TCHDesignPopupMenu);
var
  i, k, iAction: Integer;
  oItem, oParent: TMenuItem;
  oList: TStrings;
  oMenus: TDesignMenuObjectList;
  bSelected: Boolean;
begin
  With APopupMenu Do begin
    Items.Clear;
    if Not Pause then
    begin
      AddControlPopupMenu(APopupMenu);
      AddSeparatorPopupMenu(APopupMenu, Nil);
      AddPositionPopupMenu(APopupMenu);
      AddTabOrderPopupMenu(APopupMenu);
      AddCopyPastePopupMenu(APopupMenu);

      bSelected := (Selection.Count > 0) and (Not Pause);
      MenuItemByTag(miControlMenu).Enabled := bSelected;
      MenuItemByTag(miPositionMenu).Enabled:= bSelected;
      MenuItemByTag(miTabOrder).Enabled    := (Selection.Count = 0);
      if Selection.Count = 0 then
        FTabControl := ComponentsControl
      else
        FTabControl := Nil;
    end
    else begin
      oMenus := TDesignMenuObjectList.Create;
      oList := TStringList.Create;
      Try
        if Selection[0] is TCHGrid then
        begin
          TCHGrid(Selection[0]).GetDesignMenu(oMenus);
        end
        else if Selection[0] is TCHImage then
        begin
          TCHImage(Selection[0]).GetDesignMenu(oMenus);
        end;
        For i := 0 To oMenus.Count - 1 Do begin
          oList.Text := StringReplace(oMenus[i].Caption, '|', #13#10, [rfReplaceAll, rfIgnoreCase]);
          oParent := Items;
          For k := 0 To oList.Count - 1 Do begin
            if SameText(oList[k], '-') then
              iAction := miSeparator
            else if k < oList.Count - 1 then
              iAction := miParentMenu
            else
              iAction := oMenus[i].Tag;
            if iAction = miSeparator then
              oItem := Nil
            else
              oItem := MenuItemByCaption(oParent, oList[k]);
            if Not Assigned(oItem) then
              oParent := AddControlDesignPopupMenu(APopupMenu, oParent, oList[k], iAction)
            else
              oParent := oItem;
            if k = oList.Count - 1 then
            begin
              if oMenus[i].RadioItem then
                oParent.RadioItem := oMenus[i].RadioItem;
              if oMenus[i].Checked then
                oParent.Checked := oMenus[i].Checked;
            end;
          end;
        end;
      Finally
        oMenus.Free;
        oList.Free;
      End;
    end;
  end;
end;

constructor TCHDesigner.Create(AOwner: TComponent);
begin
  if Not (AOwner is TControl) then
    Raise Exception.Create('TCHDesigner must be placed onto a TControl instance');
  if CHDsCount.DesignerInControl(TControl(AOwner)) then
    Raise Exception.Create('Control should have only a single TCHDesigner');
  inherited Create(AOwner);
  FActive := False;
  FStepToGrid := 8;
  FPause := False;
  FReSizeEdgeWidth := 5;
  CHDsCount.Add(Self);
  FDownPoint := Point(0, 0);
  FStartP:= Point(-1, -1);
  FEndP  := Point(-1, -1);
  FSelection:= TCHControlList.Create;
  FSortList := TStringList.Create;
  FSortList.Sorted := True;
  FPopupMenuImages := TImageList.Create(Self);
  With FPopupMenuImages Do begin
    Width := 8;
    Height:= 16;
  end;
  FPopupMenu := TCHDesignPopupMenu.Create(Self);
  With FPopupMenu Do begin
    Images := FPopupMenuImages;
  end;
  FUsePopupMenu := True;
  FUseObjectInspector := True;
  FDesignClassNames := TStringList.Create;
  With FDesignClassNames Do
  begin
  {  Add('TCHEdit');
    Add('TCHDateTime');
    Add('TCHCheckList');
    Add('TCHGrid');
    Add('TCHLabel');
    Add('TCHEleEdit');
    Add('TmwCustomEdit');
    Add('TCHPanel');
    Add('TCHImage');
    Add('TCHShape');
    Add('TCHBevel');
    Add('TCHSeal');
    Add('TPub_CHChildBase');
    Add('TPub_CHChild');
   }
    Add('TLDSEdit');
    Add('TLDSLabel');
    Add('TLDSPanel');
    Add('TLDSCheckBox');

  end;
  FComponentsControl := Nil;
  FFmInspector := Nil;
  FCopyList := TCHControlList.Create;
end;

destructor TCHDesigner.Destroy;
begin
  Active := False;
  CHDsCount.Delete(Self);
  FSelection.Free;
  FSortList.Free;
  FPopupMenuImages.Free;
  FPopupMenu.Free;
  FDesignClassNames.Free;
  if Assigned(FFmInspector) then
    FreeAndNil(FFmInspector);
  FCopyList.Free;
  inherited Destroy;
end;

function TCHDesigner.GetComponentsControl: TWinControl;
begin
  if (Not Assigned(FComponentsControl)) and Assigned(FOnGetComponentsControl) then
  begin
    FOnGetComponentsControl(Self, FComponentsControl);
  end;
  Result := FComponentsControl;
end;

procedure TCHDesigner.SetComponentsControl(const Value: TWinControl);
begin
  FComponentsControl := Value;
end;

procedure TCHDesigner.InitDesigner;
begin
  DelSelection(Nil, True);
  ComponentsControl := Nil;
  if Assigned(FFmInspector) then
  begin
    With TFmInspector(FmInspector) Do begin
      ObjectList.Clear;
      rmCHInspector.ClearItems;
    end;
  end;
end;

function InspectorSortListCompare(List: TStringList; Index1, Index2: Integer): Integer;
var
  iPos: Integer;
  S1, S2, H1, H2: String;
begin
  if (Index1 >= 0) and (Index2 >= 0) then
  begin
    S1 := List[Index1];
    S2 := List[Index2];
    iPos := Pos(':', S1);
    H1 := Trim(Copy(S1, 1, iPos - 1));
    iPos := Pos(':', S2);
    H2 := Trim(Copy(S2, 1, iPos - 1));
    if ((H1 = '') and (H2 = '')) or ((H1 <> '') and (H2 <> '')) then
    begin
      Result := CompareText(S1, S2);
    end
    else if H1 = '' then
    begin
      Result := 1;
    end
    else begin
      Result := -1;
    end;
  end
  else begin
    Result := 0;
  end;
end;

procedure TCHDesigner.RefreshObjectList;
var
  i, iSortListIndex: Integer;
  S, sHisName: String;
  eEvent: TNotifyEvent;
  oList: TStringList;
begin
  if Assigned(ComponentsControl) then
  begin
    oList := TStringList.Create;
    Try
      oList.Clear;
      With ComponentsControl Do begin
        iSortListIndex := TFmInspector(FmInspector).SortListIndex;
        For i := 0 To ControlCount - 1 Do begin
          if IsCHClass(Controls[i]) then
          begin
            sHisName := GetObjectHisName(Controls[i]);
            if iSortListIndex = 0 then
              oList.AddObject(Format('%S : %S', [Controls[i].Name, sHisName]), Controls[i])
            else
              oList.AddObject(Format('%S : %S', [sHisName, Controls[i].Name]), Controls[i]);
          end;
        end;
        oList.CustomSort(@InspectorSortListCompare);
        sHisName := GetObjectHisName(ComponentsControl);
        if iSortListIndex = 0 then
          oList.Insert(0, Format('%S : %S', [Name, sHisName]))
        else
          oList.Insert(0, Format('%S : %S', [sHisName, Name]));
        oList.Objects[0] := ComponentsControl;
      end;
      With TFmInspector(FmInspector) Do begin
        S := ObjectList.Text;
        ObjectList.Items.Assign(oList);
        i := ObjectList.Items.IndexOf(S);
        if i < 0 then
        begin
          ObjectList.ItemIndex := Integer(ObjectList.Items.Count > 0) - 1;
        end
        else begin
          eEvent := ObjectList.OnChange;
          ObjectList.OnChange := Nil;
          Try
            ObjectList.ItemIndex := i;
          Finally
            ObjectList.OnChange := eEvent;
          End;
        end;
        if Assigned(ObjectList.OnChange) then
        begin
          ObjectList.OnChange(ObjectList);
        end;
      end;
    Finally
      oList.Free;
    End;
  end;
end;

procedure TCHDesigner.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if Operation = opRemove then
  begin
    if AComponent = FMoveControl then
      FMoveControl := nil;
  end;
end;

function TCHDesigner.IsCHClass(AControl: TControl): Boolean;
begin
  With DesignClassNames Do
  begin
    Result := (IndexOf(AControl.ClassName) >= 0);
  end;
end;

function TCHDesigner.IsContainer(AControl: TControl): Boolean;
begin
  Result := (AControl is TCustomForm) or
            (AControl is TCustomPanel);
end;

procedure TCHDesigner.SetDesignClassNames(Value: TStrings);
begin
  if Assigned(Value) then
    FDesignClassNames.Assign(Value);
end;

function TCHDesigner.GetOwnerControl: TControl;
begin
  Result := TControl(Owner);
end;

procedure TCHDesigner.SetStepToGrid(Value: Integer);
begin
  FStepToGrid := Value;
  if FStepToGrid < 1 then
    FStepToGrid := 1;
end;

procedure TCHDesigner.SetActive(Value: Boolean);
begin
  if FActive <> Value then
  begin
    if Value and Assigned(CHDsCount.ActiveDesigner) and
       (CHDsCount.ActiveDesigner <> Self) and CHDsCount.ActiveDesigner.Active then
      CHDsCount.ActiveDesigner.Active := False;
    FActive := Value;
    if Active then
    begin
      CHDsCount.ActiveDesigner := Self;
      Application.OnMessage := ApplicationMessages;
    end
    else begin
      if CHDsCount.ActiveDesigner = Self then
        Application.OnMessage := Nil;
    end;
    if (Not Active) and Assigned(FFmInspector) then
    begin
      With TFmInspector(FmInspector) Do begin
        Close;
        FreeAndNil(FFmInspector);
      end;
    end;
  end;
end;

function TCHDesigner.IsSelection(AControl: TControl): Boolean;
begin
  Result := (Selection.IndexOf(AControl) >= 0);
end;

function TCHDesigner.IsCHWinControl(AControl: TControl): Boolean;
begin
  Result := (AControl is TCHPanel);
end;

function TCHDesigner.AddSelection(AControl: TControl): Integer;
var
  P: PPropInfo;
begin
  Result := Selection.IndexOf(AControl);
  if Result < 0 then
  begin
    Result := Selection.Add(AControl);
    P := GetPropInfo(AControl, 'DesignState');
    if Assigned(P) then
      SetOrdProp(AControl, P, Ord(True));
    if Assigned(FOnSelectCHControl) then
      FOnSelectCHControl(Self, Selection);
  end;
end;

procedure TCHDesigner.DelSelection(AControl: TControl; DelAll: Boolean = False);
var
  P: PPropInfo;
  bi, ei: Integer;
begin
  if Not DelAll then
  begin
    bi := Selection.IndexOf(AControl);
    ei := bi;
  end
  else begin
    bi := 0;
    ei := Selection.Count - 1;
  end;
  if (bi >= 0) and (ei >= 0) then
  begin
    While ei >= bi Do begin
      P := GetPropInfo(Selection[ei], 'DesignState');
      if Assigned(P) then
        SetOrdProp(Selection[ei], P, Ord(False));
      Selection.Delete(ei);
      Dec(ei);
    end;
  end;
end;

function TCHDesigner.MoveSelection(Dx, Dy: Integer; var P: TPoint; KeyMove: Boolean = False): Boolean;
var
  i, Rx, Ry, iLeft, iTop: Integer;
begin
  Result := False;
  Rx := 0;
  Ry := 0;
  if Not KeyMove then
  begin
    Rx := Dx Mod StepToGrid;
    Dx := (Dx Div StepToGrid) * StepToGrid;
    Ry := Dy Mod StepToGrid;
    Dy := (Dy Div StepToGrid) * StepToGrid;
  end;
  For i := 0 To Selection.Count - 1 Do begin
    if Selection[i].Align = alNone then
    begin
      With Selection[i] Do begin
        if (Dx <> 0) or (Dy <> 0) then
        begin
          Result := True;
          iLeft:= Left + Dx;
          iTop := Top + Dy;
          if Assigned(FOnCHControlMove) then
            FOnCHControlMove(Selection[i], iLeft, iTop);
          Left := iLeft;
          Top  := iTop;
        end;
      end;
    end;
  end;
  if (Not KeyMove) and Result then
  begin
    P.x := P.x  - Rx;
    P.y := P.y  - Ry;
    SetCursorPos(P.x, P.y);
  end;
end;

function TCHDesigner.ReSizeSelection(Dx, Dy: Integer; var P: TPoint; KeyMove: Boolean = False): Boolean;
var
  oForm: TCustomForm;
  i, Rx, Ry: Integer;
  ALeft, ATop, AWidth, AHeight: Integer;
begin
  Result := False;
  oForm := Nil;
  Rx := 0;
  Ry := 0;
  For i := 0 To Selection.Count - 1 Do begin
    With Selection[i] Do begin
      Rx := 0;
      Ry := 0;
      if Not KeyMove then
      begin
        Rx := Dx Mod StepToGrid;
        Dx := (Dx Div StepToGrid) * StepToGrid;
        Ry := Dy Mod StepToGrid;
        Dy := (Dy Div StepToGrid) * StepToGrid;
      end;
      ALeft  := Left;
      ATop   := Top;
      AWidth := Width;
      AHeight:= Height;
      if (Dx <> 0) or (Dy <> 0) then
      begin
        Case FMode Of
          1:
          begin
            ALeft  := ALeft + dX;
            ATop   := ATop + dY;
            AWidth := AWidth - dX;
            AHeight:= AHeight - dY;
          end;
          2:
          begin
            ATop   := ATop + dY;
            AHeight:= AHeight - dY;
          end;
          3:
          begin
            ATop   := ATop + dY;
            AWidth := AWidth + dX;
            AHeight:= AHeight - dY;
          end;
          4:
          begin
            AWidth := AWidth + dX;
          end;
          5:
          begin
            AWidth := AWidth + dX;
            AHeight := AHeight + dY;
          end;
          6:
          begin
            AHeight := AHeight + dY;
          end;
          7:
          begin
            ALeft  := ALeft + dX;
            AWidth := AWidth - dX;
            AHeight:= AHeight + dY;
          end;
          8:
          begin
            ALeft := ALeft + dX;
            AWidth:= AWidth - dX;
          end;
        End;
        AWidth := Max(AWidth, ControlMin);
        AHeight:= Max(AHeight,ControlMin);
        if Assigned(FOnReSizeSelection) then
          FOnReSizeSelection(Self, FMode, ALeft, ATop, AWidth, AHeight);
        Left  := ALeft;
        Top   := ATop;
        Width := AWidth;
        Height:= AHeight;
        if Selection[i] is TCustomForm then
        begin
          oForm := TCustomForm(Selection[i]);
        end;
        Result := True;
      end;
    end;
  end;
  if Assigned(oForm) then
  begin
    oForm.Invalidate;
  end;
  if (Not KeyMove) and Result then
  begin
    P.x := P.x  - Rx;
    P.y := P.y  - Ry;
    SetCursorPos(P.x, P.y);
  end;
end;

function TCHDesigner.ExistsCHName(AName: String; AControl: TControl): Boolean;
var
  i: Integer;
  oControl: TControl;
begin
  With AControl Do begin
    Result := False;
    For i := 0 To ComponentCount - 1 Do begin
      if Components[i] is TControl then
      begin
        oControl := TControl(Components[i]);
        if IsCHClass(oControl) and (AnsiCompareText(oControl.Name, AName) = 0) then
        begin
          Result := True;
          Break;
        end;
      end;
    end;
  end;
end;

procedure TCHDesigner.DeleteSelectionControl;
begin
  While Selection.Count > 0 Do begin
    if Assigned(FOnDeleteControl) then
      FOnDeleteControl(Self, Selection[0]);
    Selection[0].Free;
    Selection.Delete(0);
  end;
  ControlProperty(False);
end;

procedure TCHDesigner.CopySelectionControl;
var
  i: Integer;
  oParent: TWinControl;
  oList: TCHControlList;
begin
  if Selection.Count > 0 then
  begin
    oList := TCHControlList.Create;
    Try
      oParent := Selection[0].Parent;
      if Assigned(oParent) then
      begin
        For i := 0 To oParent.ControlCount - 1 Do begin
          if IsSelection(oParent.Controls[i]) then
          begin
            oList.Add(oParent.Controls[i]);
          end;
        end;
        if oList.Count > 0 then
        begin
          CopyList.ClearControls;
          For i := 0 To oList.Count - 1 Do begin
            CopySelectionControlRecursion(oList[i], TWinControl(OwnerControl), True);
          end;
        end;
      end;
    Finally
      oList.Free;
    End;
  end;
end;

procedure TCHDesigner.CopySelectionControlRecursion(AControl: TControl; AParent: TWinControl; ACopy: Boolean);
var
  i: Integer;
  oClass: TControlClass;
  oControl: TControl;
begin
  oClass := TControlClass(GetClass(AControl.ClassName));
  oControl := oClass.Create(AParent);
  oControl.Visible := False;
  oControl.Parent := AParent;
  oControl.Assign(AControl);
  oControl.ControlStyle :=  oControl.ControlStyle - [csSetCaption];
  oControl.Name := AControl.Name;
  oControl.ControlStyle :=  oControl.ControlStyle + [csSetCaption];
  if ACopy then
    CopyList.Add(oControl);
  if IsCHWinControl(AControl) then
  begin
    With TWinControl(AControl) Do begin
      For i := 0 To ControlCount - 1 Do begin
        CopySelectionControlRecursion(Controls[i], TWinControl(oControl), False);
      end;
    end;
  end;
end;

procedure TCHDesigner.PasteControl;
var
  i: Integer;
  oParent: TWinControl;
  eEvent: TNotifyEvent;
begin
  if CopyList.Count > 0 then
  begin
    oParent := CurrentContainer;
    DelSelection(Nil, True);
    For i := 0 To CopyList.Count - 1 Do
    begin
      PasteControlRecursion(CopyList[i], oParent, True);
    end;
    With TFmInspector(FmInspector) Do begin
      eEvent := ObjectList.OnChange;
      Try
        ObjectList.OnChange := Nil;
        RefreshObjectList;
        ControlProperty(False);
      Finally
        ObjectList.OnChange := eEvent;
      End;
    end;
  end;
end;

procedure TCHDesigner.PasteControlRecursion(AControl: TControl; AParent: TWinControl; ASelection: Boolean);
var
  i, iTop, iLeft: Integer;
  oClass: TControlClass;
  oControl: TControl;
begin
  oClass := TControlClass(GetClass(AControl.ClassName));
  oControl := oClass.Create(ComponentsControl);
  oControl.Parent := AParent;
  oControl.Assign(AControl);
  if ExistsCHName(AControl.Name, ComponentsControl) then
  begin
    oControl.Name := GetCHComponentName(oControl.ClassName);
  end
  else begin
    oControl.ControlStyle :=  oControl.ControlStyle - [csSetCaption];
    oControl.Name := AControl.Name;
    oControl.ControlStyle :=  oControl.ControlStyle + [csSetCaption];
  end;
  iTop := Min(ComponentsControl.Height - oControl.Height, oControl.Top + FStepToGrid);
  iLeft:= Min(ComponentsControl.Width - oControl.Width, oControl.Left + FStepToGrid);
  oControl.Top := iTop;
  oControl.Left:= iLeft;
  oControl.BringToFront;
  if ASelection then
    AddSelection(oControl);
  if IsCHWinControl(AControl) then
  begin
    With TWinControl(AControl) Do begin
      For i := 0 To ControlCount - 1 Do begin
        PasteControlRecursion(Controls[i], TWinControl(oControl), False);
      end;
    end;
  end;
end;

procedure TCHDesigner.GetKeyMovePos(Key: Word; var Dx, Dy: Integer; bShiftCtrl: Boolean);
var
  StepArray: Array[Boolean] Of Integer;
begin
  Dx := 0;
  Dy := 0;
  StepArray[False]:= 1;
  StepArray[True] := StepToGrid;
  Case Key Of
    VK_LEFT:
      begin
        Dx := -1 * StepArray[bShiftCtrl];
        Dy := 0;
      end;
    VK_RIGHT:
      begin
        Dx := 1 * StepArray[bShiftCtrl];
        Dy := 0;
      end;
    VK_UP:
      begin
        Dx := 0;
        Dy := -1 * StepArray[bShiftCtrl];
      end;
    VK_DOWN:
      begin
        Dx := 0;
        Dy := 1 * StepArray[bShiftCtrl];
      end;
  End;
end;

procedure TCHDesigner.GetKeySizePos(Key: Word; var Dx, Dy, pMode: Integer);
begin
  GetKeyMovePos(Key, Dx, Dy, False);
  pMode := 0;
  Case Key Of
    VK_LEFT, VK_RIGHT: pMode := 4;
    VK_UP, VK_DOWN:    pMode := 6;
  End;
end;

function TCHDesigner.GetSelRect(SP, EP: TPoint): TRect;
begin
  Result := Rect(0, 0, 0, 0);
  With Result Do begin
    if SP.x <= EP.x then
    begin
      Left := SP.x;
      Right:= EP.x;
    end
    else begin
      Left := EP.x;
      Right:= SP.x;
    end;
    if SP.y <= EP.y then
    begin
      Top   := SP.y;
      Bottom:= EP.y;
    end
    else begin
      Top   := EP.y;
      Bottom:= SP.y;
    end;
  end;
end;

procedure TCHDesigner.CHDrawFocusRect(R: TRect);
var
  DC: HDC;
begin
  DC := GetDC(0);
  Try
    DrawFocusRect(DC, R);
  Finally
    ReleaseDC(0, DC);
  End;
end;

procedure TCHDesigner.GetSelRectControls(AWinControl: TWinControl);
begin
  DelSelection(Nil, True);
  GetSelRectControlsRecursion(ComponentsControl);
  ControlProperty(False);
end;

procedure TCHDesigner.GetSelRectControlsRecursion(AWinControl: TWinControl);
var
  i: Integer;
  C: TControl;
  pt: TPoint;
  R, R1, R2: TRect;
begin
  With AWinControl Do begin
    For i := 0 To ControlCount - 1 Do begin
      if IsCHClass(Controls[i]) then
      begin
        C := Controls[i];
        pt.x := C.Left;
        pt.y := C.Top;
        pt := ClientToScreen(pt);
        R1.Left := pt.x;
        R1.Top  := pt.y;
        pt.x := C.Left + C.Width;
        pt.y := C.Top + C.Height;
        pt := ClientToScreen(pt);
        R1.Right := pt.x;
        R1.Bottom := pt.y;
        R2 := GetSelRect(FStartP, FEndP);
        IntersectRect(R, R1, R2);
        if Not IsRectEmpty(R) then
        begin
          AddSelection(C);
          if C is TWinControl then
          begin
            GetSelRectControlsRecursion(TWinControl(C));
          end;
        end;
      end;
    end;
  end;
end;

procedure TCHDesigner.ApplicationMessages(var Msg: TMsg; var Handled: Boolean);
var
  Dx, Dy: Integer;
  P: TPoint;
  bSuccess: Boolean;
  AControl: TControl;
  bShiftCtrl: Boolean;
begin
  if (Not Pause) and (Msg.Message = WM_KEYDOWN) and
     (Msg.wParam in [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_DELETE, VK_F11]) then
  begin
    if PressKey(VK_CONTROL) and (Msg.wParam = VK_DELETE) then
    begin
      DeleteSelectionControl;
    end
    else if ( (PressKey(VK_CONTROL) and PressKey(VK_SHIFT)) or
              (PressKey(VK_CONTROL) and Not PressKey(VK_SHIFT)) ) and
            (Selection.Count > 0) then
    begin
      bShiftCtrl := PressKey(VK_SHIFT) and PressKey(VK_CONTROL);
      GetKeyMovePos(Msg.wParam, Dx, Dy, bShiftCtrl);
      P.X := 0;
      P.Y := 0;
      MoveSelection(Dx, Dy, P, True);
      Msg.Message := 0;
    end
    else if (Not PressKey(VK_CONTROL)) and PressKey(VK_SHIFT) then
    begin
      GetKeySizePos(Msg.wParam, Dx, Dy, FMode);
      P.X := 0;
      P.Y := 0;
      ReSizeSelection(Dx, Dy, P, True);
      Msg.Message := 0;
    end;
  end
  else if (Not Pause) and (Msg.Message >= WM_MOUSEMOVE) and
          (Msg.Message < WM_RBUTTONDOWN) and InComponentsControl(Msg.hwnd) then
  begin
    P := SmallPointToPoint(TSmallPoint(Msg.lParam));
    ClientToScreen(Msg.hwnd, P);
    AControl := FindCHControl(ComponentsControl, P);
    if Not Assigned(AControl) then
      AControl := ComponentsControl;
    if Assigned(AControl) and
       (Not ( ((AControl is TCHGrid) and (TCHGrid(AControl).GridState <> gsNormal)) or
              ((AControl.Parent is TCHGrid) and (TCHGrid(AControl.Parent).GridState <> gsNormal)) )) then
    begin
      Case Msg.Message Of
        WM_LBUTTONDOWN:
          begin
            if PressKey(VK_CONTROL) then
            begin
              FStartP := P;
              FEndP   := P;
            end
            else begin
              FMode := 0;
              bSuccess := False;
              if Assigned(FOnCreateCHControl) then
              begin
                FDownPoint.X := P.X;
                FDownPoint.Y := P.Y;
                FOnCreateCHControl(CurrentContainer, P.X, P.Y, bSuccess);
                AControl := FindCHControl(ComponentsControl, P);
              end;
              FMoveControl := FindMoveControl(AControl, P, FMode);
              if (FMoveControl <> Nil) and IsCHClass(FMoveControl) then
              begin
                if PressKey(VK_SHIFT) then
                begin
                  if IsSelection(FMoveControl) then
                  begin
                    DelSelection(FMoveControl);
                    ControlProperty(False);
                  end
                  else begin
                    AddSelection(FMoveControl);
                    ControlProperty(False);
                  end;
                end
                else begin
                  if Not IsSelection(FMoveControl) then
                  begin
                    DelSelection(Nil, True);
                    AddSelection(FMoveControl);
                    ControlProperty(False);
                  end;
                end;
                FDownPoint.X := P.X;
                FDownPoint.Y := P.Y;
              end
              else if (FMoveControl <> Nil) and (Not IsCHClass(FMoveControl)) then
              begin
                if Not PressKey(VK_SHIFT) then
                begin
                  DelSelection(Nil, True);
                  ControlProperty(False);
                end
                else begin
                  FDownPoint.X := P.X;
                  FDownPoint.Y := P.Y;
                end;
              end
              else begin
                DelSelection(Nil, True);
                ControlProperty(False);
              end;
              if Selection.Count <> 1 then
                FMode := 0;
            end;
          end;
        WM_MOUSEMOVE:
          begin
            if Msg.WParam and MK_LBUTTON = MK_LBUTTON then
            begin
              if FStartP.x >= 0 then
              begin
                CHDrawFocusRect(GetSelRect(FStartP, FEndP));
                FEndP := P;
                CHDrawFocusRect(GetSelRect(FStartP, FEndP));
              end
              else begin
                if (Selection.Count > 0) and (FMode = 0) then
                begin
                  Dx := P.X - FDownPoint.X;
                  Dy := P.Y - FDownPoint.Y;
                  if MoveSelection(Dx, Dy, P) then
                  begin
                    FDownPoint.X := P.X;
                    FDownPoint.Y := P.Y;
                  end;
                end
                else if (Selection.Count = 1) and (FMode in [1..8]) then
                begin
                  With Selection[0] Do begin
                    Dx := P.X - FDownPoint.X;
                    Dy := P.Y - FDownPoint.Y;
                    if ReSizeSelection(Dx, Dy, P) then
                    begin
                      FDownPoint.X := P.X;
                      FDownPoint.Y := P.Y;
                    end;
                    SetMyCursor(Selection[0], FMode);
                    SetMyCursor(Selection[0].Parent, FMode);
                  end;
                end;
              end;
            end
            else begin
              if Selection.Count = 1 then
              begin
                FMode := 0;
                if Selection[0] = FindMoveControl(AControl, P, FMode) then
                  SetMyCursor(Selection[0], FMode)
                else
                  SetMyCursor(Selection[0], 9);
                SetMyCursor(Selection[0].Parent, 9);
              end;
            end;
          end;
        WM_LBUTTONUP:
        begin
          if FStartP.x >= 0 then
          begin
            CHDrawFocusRect(GetSelRect(FStartP, FEndP));
            GetSelRectControls(ComponentsControl);
          end;
          FStartP := Point(-1, -1);
          FEndP   := Point(-1, -1);
          if Selection.Count = 1 then
          begin
            SetMyCursor(Selection[0], 9);
            SetMyCursor(Selection[0].Parent, 9);
          end;
        end;
      End;
    end;
  end
  else if UsePopupMenu and (Msg.Message = WM_RBUTTONDOWN) then
  begin
    if Assigned(OwnerControl) and (OwnerControl is TForm) and (TForm(OwnerControl).Active) then
    begin
      P := SmallPointToPoint(TSmallPoint(Msg.lParam));
      ClientToScreen(Msg.hwnd, P);
      AControl := FindCHControl(ComponentsControl, P);
      if Not Assigned(AControl) then
        AControl := ComponentsControl;
      FPopupControl := FindMoveControl(AControl, P, FMode);
      With FPopupMenu Do begin
        Popup(P.x, P.y);
      end;
    end;
  end;
end;

function TCHDesigner.InComponentsControl(AHandle: HWnd): Boolean;
begin
  Result := False;
  While AHandle <> 0 Do begin
    if AHandle = ComponentsControl.Handle then
    begin
      Result := True;
      Break;
    end;
    AHandle := GetParent(AHandle);
  end;
end;

function TCHDesigner.FindCHControl(AWinControl: TWinControl; P: TPoint): TControl;
var
  i: Integer;
  pt: TPoint;
  R1: TRect;
  C, oParent: TControl;
begin
  Result := Nil;
  if Assigned(AWinControl) then
  begin
    With AWinControl Do begin
      i := ControlCount - 1;
      While i >= 0 Do begin
        if IsCHClass(Controls[i]) then
        begin
          C := Controls[i];
          if C is TWinControl then
          begin
            Result := FindCHControl(TWinControl(C), P);
          end;
          if Assigned(Result) then
          begin
            Break;
          end;
          oParent := C.Parent;

          pt.x := C.Left;
          pt.y := C.Top;
          pt := oParent.ClientToScreen(pt);
          R1.Left := pt.x;
          R1.Top  := pt.y;

          pt.x := C.Left + C.Width;
          pt.y := C.Top + C.Height;
          pt := oParent.ClientToScreen(pt);
          R1.Right := pt.x;
          R1.Bottom := pt.y;
          
          if PtInRect(R1, P) then
          begin
            Result := C;
            Break;
          end;
        end;
        Dec(i);
      end;
    end;
  end;
end;

function TCHDesigner.FindMoveControl(AControl: TControl; P: TPoint; var Mode: Integer): TControl;
var
  R: TRect;
  oControl: TControl;
  D, iMW, iMH: Integer;
begin
  Result := Nil;
  if Assigned(AControl) then
  begin
    oControl := AControl;
    While Assigned(oControl) Do begin
      if IsCHClass(oControl) then
      begin
        Result := oControl;
        Break;
      end;
      oControl := oControl.Parent;
    end;
  end;
  if Assigned(Result) then
  begin
    With Result Do begin
      Mode := 0;
      D := ReSizeEdgeWidth;
      if Result is TWinControl then
      begin
        GetWindowRect(TWinControl(Result).Handle, R);
      end
      else if Assigned(Parent) then
      begin
        R.TopLeft := Parent.ClientToScreen(BoundsRect.TopLeft);
        R.BottomRight := Parent.ClientToScreen(BoundsRect.BottomRight);
      end
      else begin
        R := BoundsRect;
      end;
      iMW := R.Left + ((R.Right - R.Left - D) Div 2);
      iMH := R.Top + (R.Bottom - R.Top - D) Div 2;
      if PtInRect(Rect(R.Left, R.Top, R.Left + D, R.Top + D), P) then
        Mode := 1;  // top left corner
      if PtInRect(Rect(iMW, R.Top, iMW + D, R.Top + D), P) then
        Mode := 2;  // top edge
      if PtInRect(Rect(R.Right - D, R.Top, R.Right, R.Top + D), P) then
        Mode := 3;  // top right corner
      if PtInRect(Rect(R.Right - D, iMH, R.Right, iMH + D), P) then
        Mode := 4;  // right edge
      if PtInRect(Rect(R.Right - D, R.Bottom - D, R.Right, R.Bottom), P) then
        Mode := 5;  // bottom right corner
      if PtInRect(Rect(iMW, R.Bottom - D, iMW + D, R.Bottom), P) then
        Mode := 6;  // bottom edge
      if PtInRect(Rect(R.Left, R.Bottom - D, R.Left + D, R.Bottom), P) then
        Mode := 7;  // bottom left corner
      if PtInRect(Rect(R.Left, iMH, R.Left + D, iMH + D), P) then
        Mode := 8;  // left edge
      if (Align = alClient) or (Anchors = [akLeft..akBottom]) then
        Mode := 0;
    end;
  end;
end;

procedure TCHDesigner.SetMyCursor(AControl: TControl; Mode: Integer);
begin
  if AControl = nil then
    Exit;
  case Mode of
    1, 5: AControl.Cursor := crSizeNWSE;
    2, 6: AControl.Cursor := crSizeNS;
    3, 7: AControl.Cursor := crSizeNESW;
    4, 8: AControl.Cursor := crSizeWE;
  else
    AControl.Cursor := crDefault;
  end;
end;

procedure TCHDesigner.ControlAlignToGrid;
var
  i: Integer;
begin
  For i := 0 To Selection.Count - 1 Do begin
    With Selection[i] Do begin
      if Align = alNone then
      begin
        Left:= (Left Div StepToGrid) * StepToGrid;
        Top := (Top Div StepToGrid) * StepToGrid;
      end;
    end;
  end;
end;

procedure TCHDesigner.ControlBringToFront;
var
  i: Integer;
begin
  For i := 0 To Selection.Count - 1 Do begin
    With Selection[i] Do begin
      BringToFront;
    end;
  end;
end;

procedure TCHDesigner.ControlSendToBack;
var
  i: Integer;
begin
  For i := 0 To Selection.Count - 1 Do begin
    With Selection[i] Do begin
      SendToBack;
    end;
  end;
end;

procedure TCHDesigner.ControlAlign;
var
  i, k: Integer;
  R: TRect;
begin
  With TFmCHAlignment.Create(Self) Do begin
    Try
      if ShowModal = mrOK then
      begin
        Case RadioGroupH.ItemIndex Of
          1:
            begin
              With Selection[0] Do begin
                R.Left := Left;
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Left:= R.Left;
                end;
              end;
            end;
          2:
            begin
              With Selection[0] Do begin
                R.Left := Left + (Width Div 2);
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Left := Left + R.Left - (Left + (Width Div 2));
                end;
              end;
            end;
          3:
            begin
              With Selection[0] Do begin
                R.Left := Left + Width;
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Left := Left + R.Left - (Left + Width);
                end;
              end;
            end;
          4:
            begin
              if Selection.Count > 1 then
              begin
                R.Left := ControlMax;
                R.Right:= 0;
                k := 0;
                FSortList.Clear;
                For i := 0 To Selection.Count - 1 Do begin
                  With Selection[i] Do begin
                    R.Left := Min(R.Left, Left);
                    R.Right:= Max(R.Right, Left + Width);
                    k := k + Width;
                    FSortList.AddObject(StringReplace(Format('%4.4D', [Left]), ' ', '0', [rfReplaceAll, rfIgnoreCase]),
                                        Selection[i]);
                  end;
                end;
                if k > 0 then
                begin
                  k := (R.Right - R.Left - k) Div (Selection.Count - 1);
                  R.Left := TControl(FSortList.Objects[0]).Left;
                  For i := 0 To FSortList.Count - 1 Do begin
                    With TControl(FSortList.Objects[i]) Do begin
                      Left := R.Left;
                      R.Left := R.Left + Width + k;
                    end;
                  end;
                end;
              end;
            end;
          5:
            begin
              With OwnerControl Do begin
                R.Left := Width Div 2;
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Left := Left + R.Left - (Left + (Width Div 2));
                end;
              end;
            end;
        End;
        Case RadioGroupV.ItemIndex Of
          1:
            begin
              With Selection[0] Do begin
                R.Top := Top;
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Top:= R.Top;
                end;
              end;
            end;
          2:
            begin
              With Selection[0] Do begin
                R.Top := Top + (Height Div 2);
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Top := Top + R.Top - (Top + (Height Div 2));
                end;
              end;
            end;
          3:
            begin
              With Selection[0] Do begin
                R.Top := Top + Height;
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Top := Top + R.Top - (Top + Height);
                end;
              end;
            end;
          4:
            begin
              if Selection.Count > 1 then
              begin
                R.Top := ControlMax;
                R.Bottom := 0;
                k := 0;
                FSortList.Clear;
                For i := 0 To Selection.Count - 1 Do begin
                  With Selection[i] Do begin
                    R.Top := Min(R.Top, Top);
                    R.Bottom := Max(R.Bottom, Top + Height);
                    k := k + Height;
                    FSortList.AddObject(StringReplace(Format('%4.4D', [Top]), ' ', '0', [rfReplaceAll, rfIgnoreCase]),
                                        Selection[i]);
                  end;
                end;
                if k > 0 then
                begin
                  k := (R.Bottom - R.Top - k) Div (Selection.Count - 1); 
                  R.Top := TControl(FSortList.Objects[0]).Top;
                  For i := 0 To FSortList.Count - 1 Do begin
                    With TControl(FSortList.Objects[i]) Do begin
                      Top := R.Top;
                      R.Top := R.Top + Height + k;
                    end;
                  end;
                end;
              end;
            end;
          5:
            begin
              With OwnerControl Do begin
                R.Top := Height Div 2;
              end;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  Top := Top + R.Top - (Top + (Height Div 2));
                end;
              end;
            end;
        End;
      end;
    Finally
      Release;
    End;
  end;
end;

procedure TCHDesigner.ControlSize;
var
  i, k: Integer;
begin
  With TFmCHSize.Create(Self) Do begin
    Try
      if ShowModal = mrOK then
      begin
        k := 0;
        Case RadioGroupW.ItemIndex Of
          1:
            begin
              k := ControlMax;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  k := Min(k, Width);
                end;
              end;
            end;
          2:
            begin
              k := 0;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  k := Max(k, Width);
                end;
              end;
            end;
          3:
            begin
              if Length(EditWidth.Text) > 0 then
                k := Max(Min(StrToInt(EditWidth.Text), ControlMax), ControlMin)
              else
                k := ControlMin;
            end;
        End;
        if k > 0 then
        begin
          For i := 0 To Selection.Count - 1 Do begin
            With Selection[i] Do begin
              Width := k;
            end;
          end;
        end;

        k := 0;
        Case RadioGroupH.ItemIndex Of
          1:
            begin
              k := ControlMax;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  k := Min(k, Height);
                end;
              end;
            end;
          2:
            begin
              k := 0;
              For i := 0 To Selection.Count - 1 Do begin
                With Selection[i] Do begin
                  k := Max(k, Height);
                end;
              end;
            end;
          3:
            begin
              if Length(EditHeight.Text) > 0 then
                k := Max(Min(StrToInt(EditHeight.Text), ControlMax), ControlMin)
              else
                k := ControlMin;
            end;
        End;
        if k > 0 then
        begin
          For i := 0 To Selection.Count - 1 Do begin
            With Selection[i] Do begin
              Height := k;
            end;
          end;
        end;
      end;
    Finally
      Release;
    End;
  end;
end;

procedure TCHDesigner.ControlTabOrder;
var
  i: Integer;
begin
  With TFmTabOrder.Create(Self) Do begin
    Try
      TabControl := TWinControl(FTabControl);
      if ShowModal = mrOK then
      begin
        For i := 0 To TabOrderList.Items.Count - 1 Do begin
          TWinControl(TabOrderList.Items[i].Data).TabOrder := i;
        end;
      end;
    Finally
      Release;
    End;
  end;
end;

procedure TCHDesigner.ObjectListChange(Sender: TObject);
var
  oControl: TControl;
begin
  With Sender As TComboBox Do begin
    if ItemIndex >= 0 then
    begin
      DelSelection(Nil, True);
      oControl := TControl(Items.Objects[ItemIndex]);
      AddSelection(oControl);
      ControlProperty(False);
      if oControl = ComponentsControl then
      begin
        DelSelection(Nil, True);
      end;
    end;
  end;
end;

procedure TCHDesigner.ControlProperty(bForce: Boolean = True);
var
  eEvent: TNotifyEvent;
begin
  if UseObjectInspector then
  begin
    With TFmInspector(FmInspector) Do begin
      if ObjectList.Items.Count = 0 then
      begin
        eEvent := ObjectList.OnChange;
        Try
          ObjectList.OnChange := Nil;
          RefreshObjectList;
        Finally
          ObjectList.OnChange := eEvent;
        End;
      end;
      if Visible or bForce then
      begin
        if Selection.Count = 0 then
        begin
          InspectComponent := ComponentsControl;
        end
        else begin
          InspectComponent := Selection;
        end;
        if Not Visible then Show;
        if bForce and (rmCHInspector.Items.Count > 0) then
          rmCHInspector.ItemIndex := 0;
      end;
    end;
  end;
end;

procedure TCHDesigner.ControlDesigning;
begin
  Pause := Not Pause;
  if Assigned(FOnDesignCHControl) then
    FOnDesignCHControl(Self, Pause);
  if TControl(Selection[0]) is TCHEleEdit then
  begin
    With TCHEleEdit(Selection[0]) Do begin
      if Pause then
      begin
        ToolBarOptions := ToolBarOptions + [EleCustomEdit.tboDefineFont, EleCustomEdit.tboNewSegment];
        KeyWordEdit := True;
        Invalidate;
      end
      else begin
        ToolBarOptions := ToolBarOptions - [EleCustomEdit.tboDefineFont, EleCustomEdit.tboNewSegment];
        KeyWordEdit := False;
        Invalidate;
      end;
    end;
  end
  else if TControl(Selection[0]) is TCHGrid then
  begin
    With TCHGrid(Selection[0]) Do begin
      if Pause then
      begin
        ShowObjectMark := True;
        Options := Options + [goEditing, goColSizing, goRowSizing, goRangeSelect];
      end
      else begin
        ShowObjectMark := False;
        Options := Options - [goEditing, goColSizing, goRowSizing, goRangeSelect];
      end;
    end;
  end;
end;

procedure TCHDesigner.PopupMenuClick(Sender: TObject);
begin
  if Not Pause then
  begin
    Case TMenuItem(Sender).Tag Of
      miAlignToGrid:    ControlAlignToGrid;
      miBringToFront:   ControlBringToFront;
      miSendToBack:     ControlSendToBack;
      miAlign:          ControlAlign;
      miSize:           ControlSize;
      miTabOrder:       ControlTabOrder;
      miCopyControl:    CopySelectionControl;
      miPasteControl:   PasteControl;
      miCutControl:
        begin
          CopySelectionControl;
          DeleteSelectionControl;
        end;
    End;
  end
  else begin
    if (TMenuItem(Sender).Tag <> miSeparator) and (TMenuItem(Sender).Tag <> miParentMenu) then
    begin
      if Selection[0] is TCHGrid then
      begin
        TCHGrid(Selection[0]).DoDesign(TMenuItem(Sender).Tag);
      end
      else if Selection[0] is TCHImage then
      begin
        TCHImage(Selection[0]).DoDesign(TMenuItem(Sender).Tag);
      end;
    end;
  end;
end;

function TCHDesigner.GetFmInspector: TForm;
begin
  if Not Assigned(FFmInspector) then
  begin
    FFmInspector := TFmInspector.Create(Self);
    With TFmInspector(FFmInspector) Do begin
      OnSortList := InspectorSortList;
      if Not Assigned(ObjectList.OnChange) then
        ObjectList.OnChange := ObjectListChange;
    end;
  end;
  Result := FFmInspector;
end;

function TCHDesigner.GetCurrentContainer: TWinControl;
begin
  if (Selection.Count = 1) and (Selection[0] is TCHPanel) then
  begin
    Result := TWinControl(Selection[0]);
  end
  else begin
    Result := ComponentsControl;
  end;
end;

function TCHDesigner.GetObjectHisName(AObject: TObject): String;
var
  P: PPropInfo;
begin
  P := GetPropInfo(AObject, 'HisName');
  if Assigned(P) then
  begin
    Result := GetStrProp(AObject, P);
  end
  else begin
    Result := '';
  end;
end;

procedure TCHDesigner.InspectorSortList(Sender: TObject);
begin
  RefreshObjectList;
end;

{ TCHDesignPopupMenu }

constructor TCHDesignPopupMenu.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

destructor TCHDesignPopupMenu.Destroy;
begin
  inherited Destroy;
end;

procedure TCHDesignPopupMenu.DoPopup(Sender: TObject);
begin
  inherited DoPopup(Sender);
  if Owner is TCHDesigner then
  begin
    TCHDesigner(Owner).InitPopupMenu(Self);
  end;
end;

function TCHDesignPopupMenu.MenuItemByCaption(AItem: TMenuItem; ACaption: String): TMenuItem;
var
  i: Integer;
begin
  Result := Nil;
  For i := 0 To AItem.Count - 1 Do begin
    if SameText(AItem[i].Caption, ACaption) then
    begin
      Result := AItem[i];
      Break;
    end;
  end;
end;

function TCHDesignPopupMenu.MenuItemByTag(ATag: Integer): TMenuItem;
var
  i: Integer;
begin
  Result := Nil;
  For i := 0 To Items.Count - 1 Do begin
    if Items[i].Tag = ATag then
    begin
      Result := Items[i];
      Break;
    end;
  end;
end;

{ TCHControlList }

constructor TCHControlList.Create;
begin
  inherited Create;
end;

destructor TCHControlList.Destroy;
begin
  inherited Destroy;
end;

procedure TCHControlList.ClearControls;
var
  i: Integer;
begin
  For i := 0 To Count - 1 Do begin
    Items[i].Free;
  end;
  Clear;
end;

function TCHControlList.Get(Index: Integer): TControl;
begin
  Result := TControl(inherited Get(Index));
end;

procedure TCHControlList.Put(Index: Integer; Item: TControl);
begin
  inherited Put(Index, Item);
end;

initialization
begin
  CHDsCount := TCHDsCount.Create;
end;

finalization
begin
  CHDsCount.Free;
end;

end.
